﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

using Microsoft.WindowsAzure.ServiceRuntime;
using IO = System.IO;
using Commons = NougakudoCompanion.Commons;

namespace AdminWeb.Controllers
{
    public class AdminController : BaseController
    {

        Commons.Runtime runtime = null;

        public AdminController()
        {
            ViewBag.CurrentTab = "Admin";
            ViewBag.MachineName = Environment.MachineName;
            ViewBag.InstanceId = RoleEnvironment.CurrentRoleInstance.Id;
            runtime = Commons.Runtime.CreateInstance().Item2;
            ViewBag.Runtime = runtime;
            ViewBag.Errors = null;
        }

        //
        // GET: /Admin/

        [Authorize]
        public ActionResult Admin()
        {
            if (Request.QueryString["Subtab"] == null)
            {
                ViewBag.Subtab = "Applications";
            }
            else
            {
                ViewBag.Subtab = Request.QueryString["Subtab"];
            }

            string subtab = ViewBag.Subtab;
            // The request is log, then set refresh time. 
            if (subtab.Equals("ControllerLogs"))
                Response.AppendHeader("Refresh", "20; URL=" + Request.Url.PathAndQuery);
            else if (subtab.Equals("NougakudoLogs"))
                Response.AppendHeader("Refresh", "60; URL=" + Request.Url.PathAndQuery);

            return View();
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult InstallApplications(FormCollection form)
        {

            ViewBag.Errors = UpdateApplications(form);

            ViewBag.Subtab = "Applications";
            return View("Admin");
        }


        string[] UpdateApplications(FormCollection form)
        {
            var environment = runtime.Environment;
            environment.Name = form["RuntimeName"];
            environment.Version = form["RuntimeVersion"];
            runtime.Environment = environment;

            var applications = new Dictionary<string, Commons.Application>();
            foreach (var i in runtime.Applications.Keys)
            {
                var app = new Commons.Application()
                {
                    Name = form["App" + i],
                    Version = form["App" + i + "Version"],
                    Environment = form["App" + i + "Environment"],
                    NumOfProcess = GetNumOfProcessors(form["App" + i + "NumOfProcessors"]),
                    VirtualHost = GetVirtualHostValue(form, "App" + i)  // Add for NougakuDo 1.1.0 above.
                };
                applications[i] = app;
            }

            if (form["NewApp"].StartsWith("true"))
            {
                var app = new Commons.Application()
                {
                    Name = form["NewAppName"],
                    Version = form["NewAppVersion"],
                    Environment = form["NewAppEnvironment"],
                    NumOfProcess = GetNumOfProcessors(form["NewAppNumOfProcessors"]),
                    VirtualHost = GetVirtualHostValue(form, "NewApp")   // Add for NougakuDo 1.1.0 above.
                };
                applications[app.Name] = app;
            }

            applications = CheckVirtualHost(applications);  // Add for NougakuDo 1.1.0 above. A virtual host can establish only 1.

            runtime.Applications = applications;

            var ret = runtime.MakeConfiguration();
            if (ret.Item1)
                return new string[]{};
            else
                return new string[] {ret.Item2};

        }

        //  Add for NougakuDo 1.1.0 above.
        bool GetVirtualHostValue(FormCollection form, string name)
        {
            var virtualHost = form[name + "VirtualHost"];
            bool host = false;
            if (virtualHost != null || virtualHost != "")
            {
                if (virtualHost.Contains("true"))
                    host = true;
                else
                    bool.TryParse(virtualHost, out host);
            }
            return host;
        }

        // Add for NougakuDo 1.1.0 above. A virtual host can establish only 1.
        static Dictionary<string, Commons.Application> CheckVirtualHost(Dictionary<string, Commons.Application> applicationsInfo)
        {
            bool useVirtualHost = false;
            foreach (var name in applicationsInfo.Keys.ToArray())
            {
                var appInfo = applicationsInfo[name];
                if (useVirtualHost) appInfo.VirtualHost = false;
                if (appInfo.VirtualHost) useVirtualHost = true;
                applicationsInfo[name] = appInfo;
            }
            return applicationsInfo;
        }


        int GetNumOfProcessors(string num)
        {
            int x;
            var ret = int.TryParse(num, out x);
            if (!ret || x < 1)  // Change minimum.
                x = 1;  // default value
            return x;
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult OperateApplications(FormCollection form)
        {
            var ret = ExecuteOperation(form);
            if (ret)
            {
                ViewBag.Errors = new string[]{"Operation Error."};
            }

            return RedirectToAction("Admin", new { Subtab = "ControllerLogs" });
        }

        bool ExecuteOperation(FormCollection form)
        {
            var preOpe = "Ope-";
            var operations = form.AllKeys.Where(x => x.StartsWith(preOpe));
            var instances = Models.Helper.GetInstanceIDs(runtime);
            foreach (var instance in instances)
            {
                var prefix = preOpe + instance + "-";
                var ops = operations.Where(x => x.StartsWith(prefix));

                var opsApp = GetOperationsData(prefix, ops, form);

                var ret = OutputOperation(instance, opsApp);
                if (!ret)
                    return false;
            }
            return true;

        }

        Tuple<List<string>, List<string>, List<string>> GetOperationsData(string prefix, IEnumerable<string> operations, FormCollection form)
        {
            List<string> stops = new List<string>();
            List<string> starts = new List<string>();
            List<string> restarts = new List<string>();

            foreach (var key in operations)
            {
                var name = key.Replace(prefix, "");
                var value = form[key];
                if (value.Equals("start"))
                    starts.Add(name);
                else if (value.Equals("stop"))
                    stops.Add(name);
                else if (value.Equals("re start"))
                    restarts.Add(name);
            }

            return new Tuple<List<string>, List<string>, List<string>>(stops, starts, restarts);            
        }

        bool OutputOperation(string instance, Tuple<List<string>, List<string>, List<string>> ops)
        {
            if (ops.Item1.Count() == 0 && ops.Item2.Count == 0 && ops.Item3.Count == 0)
                return true;    // no operation
            if (instance == runtime.Environment.RoleInstanceID)
            {
                // local role operation to local file.
                var file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.STOP_REQUEST);
                var ret = MakeOperation(file, ops.Item1);
                if (!ret.Item1)
                    return false;
                file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.START_REQUEST);
                ret = MakeOperation(file, ops.Item2);
                if (!ret.Item1)
                    return false;
                file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.RESTART_REQUEST);
                ret = MakeOperation(file, ops.Item3);
                return ret.Item1;
            }
            else
            {
                // other role operation to queue storage.
                var ret = MakeOperationForOther(instance, Commons.Constants.STOP_REQUEST, ops.Item1);
                if (!ret.Item1)
                    return false;
                ret = MakeOperationForOther(instance, Commons.Constants.START_REQUEST, ops.Item2);
                if (!ret.Item1)
                    return false;
                ret = MakeOperationForOther(instance, Commons.Constants.RESTART_REQUEST, ops.Item3);
                return ret.Item1;
            }
            //return true;
        }

        /// <summary>
        /// Create a operation file for NougakuDoController.
        /// </summary>
        /// <param name="file">Operation file name</param>
        /// <param name="applications"></param>
        /// <returns></returns>
        Tuple<bool, string> MakeOperation(string file, List<string> applications)
        {
            if (applications.Count == 0)
                return new Tuple<bool, string>(true, "");
            try
            {
                if (IO.File.Exists(file))
                    IO.File.Delete(file);
                IO.File.WriteAllLines(file, applications);
                return new Tuple<bool, string>(true, "");
            }
            catch (Exception e)
            {
                return new Tuple<bool, string>(false, e.Message);
            }
        }

        /// <summary>
        /// Create a operation message.
        /// </summary>
        /// <param name="instance">Role instance id</param>
        /// <param name="file">Operation file name</param>
        /// <param name="applications"></param>
        /// <returns></returns>
        Tuple<bool, string> MakeOperationForOther(string instance, string file, List<string> applications)
        {
            if (applications.Count == 0)
                return new Tuple<bool, string>(true, "");

            var message = Commons.QueueStorageHelper.BuildMessage(file, applications);
            var ret = Commons.QueueStorageHelper.PutMessage(instance, message);
            return ret;
        }

        /// <summary>
        /// Cleanup configuration.xml.
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Cleanup(FormCollection form)
        {
            CleanupConfiguration();
            return RedirectToAction("Admin", new { Subtab = "ControllerLogs" });
        }

        void CleanupConfiguration()
        {
            Commons.BlobStorageHelper.DeleteBlob(runtime.Environment, Commons.Constants.CONFIGURATION);
            var file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.CONFIGURATION);
            DeleteConfiguration(file);
            file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.CONFIGURATION_BACKUP);
            DeleteConfiguration(file);
            file = IO.Path.Combine(runtime.Environment.RuntimeFolder, Commons.Constants.NEW_CONFIG);
            DeleteConfiguration(file);
            file = IO.Path.Combine(runtime.Environment.DownloadFolder, Commons.Constants.CONFIGURATION);
            DeleteConfiguration(file);
        }

        void DeleteConfiguration(string file)
        {
            try
            {
                if (IO.File.Exists(file))
                    IO.File.Delete(file);
            }
            catch
            {
            }
        }
    }
}
